동적 메모리 생성-mallocmalloc, strcmp, strcpy, memset, free 등 제공
head 영역 메모리 생성
리눅스 3GB 사용자 가상 메모리 중 동적 메모리는 HEAP 영역에 할당 된다.
malloc 계열 함수를 통해 동적 메모리 할당
free 함수를 통해 해당 메모리 해제
strcmp: 데이터 값 비교
strcpy: 데이터 값 복사
memset: 메모리 데이터 초기화(덮어쓰기)
메모리에 파일 매핑파일이 메모리에 올라와 있지 않는 경우 디스크에서 파일을 가져올 때, 인터럽트(시스템콜) 등의 OS가 작업 처리
성능이 저하된다.
mmap은 메모리 매핑으로 프로세스의 메모리에 매핑한다.
데이터를 직접 프로세스의 가상 주소 공간에 매핑함으로서, read나 write 함수를 사용하지 않고
프로그램 내부에서 정의한 변수를 사용해 파일에서 데이터를 읽거나 쓴다.(IPC로 사용 가능)
void *mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset)
[start+offset] ~ [start+offset+length]만큼의 물리 메모리 공간을 mapping 할것을 요청
수정-> [offset] - [offset+length]의 파일(fd)을 읽어서 addr 가상 메모리에 매핑한다.
start: 보통 NULL or 0 사용
offfset: mapping 되기 원하는 물리 메모리 지정
prot: 보호 모드 설정 PROT_READ|PROT_WRITE 로 많이 사용
PROT_READ(읽기 가능)
PROT_WRITE(쓰기 가능)
PROT_EXEC(실행 가능)
PROT_NONE(접근 불가)
flags: 메모리 주소 공간 설정
MAP_SHARED(다른 프로세스와 공유 가능)-프로세스 밖에 공간에 설정하면, 다른 프로세스도 접근 가능(?!)
MAP_PRIVATE(프로세스 내에서만 사용 가능)-가상 메모리 4GB 프로세스 공간 내에 설정(maybe user space?)
MAP_FIXED(지정된 주소로 공간 지정)
fd: device file에 대한 file descriptor
return value: 매핑된 메모리 주소 시작값
mmap의 동작 방식1. mmap 실행 시, 가상 메모리 주소에 file 주소 매핑(가상 메모리) //페이지 단위로 가상메모리에 매핑(실제 물리메모리에는 아직 안올라옴)
2. 해당 메모리 접근 시,(요구 페이징, lazy allocation) //메모리 접근시, 인터럽트 발생 후, 물리메모리에 복사
페이지 폴트 인터럽트 발생
OS에서 file data를 복사해서 물리 메모리 페이지에 넣어준다.
3. 메모리 read 시: 해당 물리 페이지 데이터를 읽는다. //메모리에만 access해서 데이터를 읽기 때문에 속도 빠름
4. 메모리 wrtie 시: 해당 물리 페이지 데이터 수정 후, 페이지 상태 flag중 dirty bit을 1로 수정 //메모리만 수정(하드디스크 수정 아직 x)
5. 파일 close 시, 물리 페이지 데이터가 file에 업데이트 된다(성능 개선) //파일 close 시, 하드디스크에 내용 수정
참고)
실은 파일 메모리 매핑 후 관리하는데 두 가지 방식이 있다.
1. 공유 메모리 방식(Shared Memory-Map)
메모리 맵 변경 시 원본 파일과 데이터가 동기화 되는 방식
2. 복사 메모리 맵 방식(Private Memory-Map)
처음 메모리 맵에 매핑 될 때, 파일의 내용을 읽어와서 복사하고, 그 이후에 동기화 하지 않는 방식
파일, 메모리 그리고 가상 메모리장점
read(), write() 시 반복적인 파일 접근을 방지하여 성능 개선
mapping된 영역은 파일 처리를 위한 lseek()을 사용하지 않고 간단한 포인터 조작으로 탐색 가능
단점
mmap은 페이지 사이즈 단위로 매핑
페이지 사이즈 단위의 정수배가 아닌 경우, 한 페이지(4kB) 정도의 공간 추가 할당 및 남은 공간을 0으로 채워주게 된다.
(1byte를 사용할 때도 4kB가 매핑됨-메모리 공간 낭비)
매핑 해제
int munmap(void* addr, size_t length)
void* addr: mapping된 물리 메모리 주소를 해제
length: mapping된 메모리의 크기(일반적으로 mmap에서 지정한값과 동일한 값을 넣음)
매핑 동기화
int msync(void* start, size_t, length, int flags);)
start: mmap()을 통해 리턴 받은 메모리 맵의 시작 주소
length: 동기화 할 길이, 시작 주소로 부터 길이를 지정
flags
MS_ASYSNC: 비동기 방식, 동기화(Memory->File)하라는 명령만 내리고 결과에 관계없이 다음 코드 실행
(동기화 안된 상태에서 다음 코드 실행 가능)
MS_SYNC: 동기 방식, 동기화(Memory->File)가 될 때까지 블럭 상태로 대기
MS_INVALIDATE: 현재 메모리 맵을 무효화하고 파일의 데이터로 갱신. (FILE->Memory)
매핑을 종료할 때 자동적으로 매핑 동기화를 하기는 함
mmap_read.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
int main(int argc, const char* argv[]){
char* filepath="mmap_test.txt";
struct stat fileInfo;
int fd;
fd=open(filepath, O_RDONLY, (mode_t)0600);
if(fd<0){
printf("can't open file\n");
exit(EXIT_FAILURE);
}
fstat(fd, &fileInfo);
printf("File size is %ji\n", (intmax_t)fileInfo.st_size);
char* map=mmap(0, fileInfo.st_size, PROT_READ, MAP_SHARED, fd, 0);
if(map==MAP_FAILED){
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
for(off_t i=0; i<fileInfo.st_size; i++){
printf("Found character %c at %ji\n", map[i], (intmax_t)i);
}
if(munmap(map, fileInfo.st_size)==-1){
close(fd);
perror("Error un-mmapping the file");
exit(EXIT_FAILURE);
}
close(fd);
return 0;
}
. . . (생략)
Found character h at 118
Found character at 119
Found character f at 120
Found character a at 121
Found character s at 122
Found character t at 123
Found character e at 124
Found character r at 125
Found character ! at 126
Found character ! at 127
Found character ! at 128
Found character
at 129
fstat(): stat 구조체에 입력받은 파일 디스크립터의 파일 정보 저장
stat 구조체의 st_size에는 해당 파일의 사이즈가 저장되어 있다.
mmap_write.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
int main(int argc, const char *argv[]){
struct stat fileInfo;
char* filepath="mmap_test.txt";
char* update="hello mmap!";
int fd=open(filepath, O_RDWR, (mode_t)0600);
if(fd==-1){
perror("Error opening file for writing");
exit(EXIT_FAILURE);
}
fstat(fd, &fileInfo);
printf("%ji\n", (intmax_t)fileInfo.st_size);
char* map=mmap(0, fileInfo.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(map==MAP_FAILED){
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
printf("%ld", strlen(update));
for(size_t i=0;i<strlen(update); i++){
printf("Writing chracter %c at %zu\n", update[i], i);
map[i]=update[i];
}
if(msync(map, fileInfo.st_size, MS_SYNC)==-1){
perror("Could not sync the file to disk");
}
if(munmap(map, fileInfo.st_size)==-1){
close(fd);
perror("Error un-mmapping the file");
exit(EXIT_FAILURE);
}
close(fd);
return 0;
}
csian@Csianui-MacBookPro linux % ./mmap_write
130
11Writing chracter h at 0
Writing chracter e at 1
Writing chracter l at 2
Writing chracter l at 3
Writing chracter o at 4
Writing chracter at 5
Writing chracter m at 6
Writing chracter m at 7
Writing chracter a at 8
Writing chracter p at 9
Writing chracter ! at 10
제일 처음에 문자 덮어 씌워진 후 저장
생각)
mmap을 사용하지 않고 open()과 write()를 사용할 시 어떤 동작이 다른가?
폰 노이만 구조에 의해서 어차피 컴퓨터가 프로세스에서 사용하는 모든 데이터는 메모리에 올라와야 한다.
open()을 통해서 storage에 있는 파일을 사용할 일이 생기면, 해당 파일 디스크리터를 배정한다.
이후, write를 통해서 파일에 작성해야 할 때, 해당 파일의 페이지를 메모리에 업로드한다.
메모리에서 해당 페이지에 작성을 한 후, 바로 storage 데이터에 메모리를 업로드한다.
이와 같이 해당 파일에 접근할 때마다, storage에 접근해야 한다.
반면 mmap을 통해 메모리에 해당 storage 파일을 매핑 시켰을 경우, 메모리에 업로드 된 이후로는 매핑 해제할 때까지,
storage에 접근할 필요가 없다.
메모리에서 작업을 수행